/*
 * DOMParentProxy.java
 *
 * Created on September 4, 2006, 12:21 PM
 *
 * (C) 2006 R. Alexander Milowski
 */

package org.infoset.xml.dom;

import java.util.ArrayList;
import java.util.List;
import org.infoset.xml.Child;
import org.infoset.xml.Element;
import org.infoset.xml.Item;
import org.infoset.xml.Parent;
import org.infoset.xml.util.TreeTraversal;
import org.w3c.dom.DOMException;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 *
 * @author alex
 */
public abstract class DOMParentProxy extends DOMNodeProxy {
   
   /** Creates a new instance of DOMParentProxy */
   public DOMParentProxy(Item item,DOMDocumentProxy docProxy) {
      super(item,docProxy);
   }
   
   public boolean hasChildNodes() {
      return ((Parent)item).size()!=0;
   }
   
   public Node appendChild(Node n) {
      if (!(n instanceof DOMNodeProxy)) {
         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Cannot append child from non-infoset backed node.");
      }
      DOMNodeProxy proxy = (DOMNodeProxy)n;
      if (proxy.item.getDocument()!=null) {
         throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,"Node is not from the same document.");
      }
      if (proxy.item instanceof Child) {
         ((Parent)item).add((Child)proxy.item);
      } else {
         throw new DOMException(DOMException.TYPE_MISMATCH_ERR,"Cannot append child of type "+n.getNodeType());
      }
      return n;
   }
   
   public Node removeChild(Node n) {
      if (!(n instanceof DOMNodeProxy)) {
         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Cannot remove child from non-infoset backed node.");
      }
      DOMNodeProxy proxy = (DOMNodeProxy)n;
      if (((Parent)item).remove(proxy.item)) {
         return n;
      } else {
         return null;
      }
   }
   
   public Node replaceChild(Node newChild,Node oldChild) {
      if (!(newChild instanceof DOMNodeProxy)) {
         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Cannot replace child from non-infoset backed node.");
      }
      if (!(oldChild instanceof DOMNodeProxy)) {
         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Cannot replace child from non-infoset backed node.");
      }
      DOMNodeProxy newProxy = (DOMNodeProxy)newChild;
      DOMNodeProxy oldProxy = (DOMNodeProxy)oldChild;
      if (newProxy.item.getDocument()!=null) {
         throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,"Node is not from the same document.");
      }
      int index = ((Parent)item).indexOf(oldProxy.item);
      if (index>=0) {
         ((Parent)item).set(index,(Child)newProxy.item);
         return newChild;
      } else {
         return null;
      }
   }
   public Node insertBefore(Node newChild,Node refChild) {
      if (!(newChild instanceof DOMNodeProxy)) {
         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Cannot replace child from non-infoset backed node.");
      }
      if (!(refChild instanceof DOMNodeProxy)) {
         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Cannot replace child from non-infoset backed node.");
      }
      DOMNodeProxy newProxy = (DOMNodeProxy)newChild;
      DOMNodeProxy refProxy = (DOMNodeProxy)refChild;
      if (newProxy.item.getDocument()!=null) {
         throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,"Node is not from the same document.");
      }
      int index = ((Parent)item).indexOf(refProxy.item);
      if (index>=0) {
         ((Parent)item).add(index,(Child)newProxy.item);
         return newChild;
      } else {
         return null;
      }
   }
   
   public Node getLastChild() {
      Parent p = (Parent)item;
      if (p.size()>0) {
         Child c = p.get(p.size()-1);
         return documentProxy.getProxy(c);
      }
      return null;
   }
   
   public Node getFirstChild() {
      Parent p = (Parent)item;
      if (p.size()>0) {
         Child c = p.get(0);
         return documentProxy.getProxy(c);
      }
      return null;
   }
   
   public NodeList getChildNodes() {
      final Parent p = (Parent)item;
      return new NodeList() {
         public int getLength() {
            return p.size();
         }
         public Node item(int index) {
            return documentProxy.getProxy(p.get(index));
         }
      };
   }

   public Node getParentNode() {
      Parent p = (Parent)item;
      return documentProxy.getProxy(p.getParent());
   }
   
   public NodeList getElementsByTagName(final String name)
   {
      final List<Item> matches = new ArrayList<Item>();
      TreeTraversal traversal = new TreeTraversal() {
         public void visit(Child c,boolean start) {
            if (start && c instanceof Element && ((Element)c).getName().getLocalName().equals(name)) {
               matches.add(c);
            }
         }
      };
      traversal.traverse((Parent)item);
      return new NodeList() {
         public int getLength() {
            return matches.size();
         }
         public Node item(int index) {
            return documentProxy.getProxy(matches.get(index));
         }
      };
   }
   
   public NodeList getElementsByTagNameNS(final String namespaceURI,final String localname)
   {
      final List<Item> matches = new ArrayList<Item>();
      TreeTraversal traversal = new TreeTraversal() {
         public void visit(Child c,boolean start) {
            if (start && c instanceof Element && 
                ((Element)c).getName().getLocalName().equals(localname) && ((Element)c).getName().getNamespaceName().toString().equals(namespaceURI)) {
               matches.add(c);
            }
         }
      };
      traversal.traverse((Parent)item);
      return new NodeList() {
         public int getLength() {
            return matches.size();
         }
         public Node item(int index) {
            return documentProxy.getProxy(matches.get(index));
         }
      };
   }
}
